package com.hourse.platform.dao.impl;

import com.hourse.platform.vo.PageList;
import com.hourse.platform.dao.BaseDao;
import org.hibernate.Session;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.hibernate.transform.Transformers;
import org.hibernate.type.Type;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Repository
public class BaseDaoImpl implements BaseDao {


    @PersistenceContext
    private EntityManager entityManager;


    /**
     * 获取当前SESSION
     *
     * @return
     */
    public Session getCurrentSession() {

        return (Session) entityManager.getDelegate();
    }

    /**
     * 通过HQL查询对象
     *
     * @param hql
     * @param map
     * @return Object
     */
    @Override
    public Object execSingleQuery(final String hql, final Map<String, Object> map) {

        Query query = getCurrentSession().createQuery(hql);

        if (null != map && map.size() > 0) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.uniqueResult();
    }

    /**
     * 通过主键查询对象
     *
     * @param clazz
     * @param id
     * @return
     */
    @Override
    public Object getObjectBySerializableKey(Class clazz, final Serializable id) {

        return getCurrentSession().get(clazz, id);
    }

    /**
     * 根据主键删除 Object
     *
     * @param clazz
     * @param id
     */
    @Override
    public void deleteObjectSerializableKey(Class clazz, final Serializable id) throws Exception {
        try {
            getCurrentSession().delete(getObjectBySerializableKey(clazz, id));
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 保存更新对象
     *
     * @param object
     * @throws Exception
     */
    @Override
    public void saveOrUpdate(Object object) throws Exception {
        try {
            getCurrentSession().saveOrUpdate(object);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    @Override
    public void merge(Object object) throws Exception {
        try {
            getCurrentSession().merge(object);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 保存
     *
     * @param obj
     * @throws Exception
     */
    @Override
    public void saveObject(Object obj) throws Exception {
        try {
            getCurrentSession().save(obj);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 更新
     *
     * @param obj
     * @throws Exception
     */
    @Override
    public void updateObject(Object obj) throws Exception {
        try {
            getCurrentSession().update(obj);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 通过HQL查询
     *
     * @param hql
     * @param map
     * @return
     */
    @Override
    public List getItemListByNativeHQL(final String hql, final Map<String, Object> map) {

        Query query = getCurrentSession().createQuery(hql);

        if (null != map && map.size() > 0) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getValue() instanceof Collection<?>) {
                    query.setParameterList(entry.getKey(), (Collection<?>) entry.getValue());
                } else if (entry.getValue() instanceof Object[]) {
                    query.setParameterList(entry.getKey(), (Object[]) entry.getValue());
                } else {
                    query.setParameter(entry.getKey(), entry.getValue());
                }
            }
        }
        return query.list();
    }

    /**
     * HQL 更新语句
     *
     * @param hql
     * @param params
     */
    @Override
    @Transactional(readOnly = true)
    public int executeUpdate(final String hql, final Map<String, Object> params) throws Exception {

        try {
            Query query = getCurrentSession().createQuery(hql);
            if (null != params && params.size() > 0) {
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    query.setParameter(entry.getKey(), entry.getValue());
                }
            }
            return query.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * SQL 更新删除语句
     *
     * @param sql
     * @param params
     */
    @Override
    public int executeSqlUpdate(final String sql, final Map<String, Object> params) throws Exception {
        try {
            Query query = getCurrentSession().createNativeQuery(sql);
            if (null != params && params.size() > 0) {
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    query.setParameter(entry.getKey(), entry.getValue());
                }
            }
            return query.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 通过SQL查询
     *
     * @param sql
     * @param params
     * @return
     */
    @Override
    public Object execSingleQueryBySQL(final String sql, final Map<String, Object> params) {

        Query query = getCurrentSession().createNativeQuery(sql);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.uniqueResult();
    }

    /**
     * 创建 NativeQuery
     *
     * @param sql
     * @param params
     * @return
     */
    @Override
    public NativeQuery createNativeQuery(String sql, Map<String, Object> params) {

        NativeQuery query = getCurrentSession().createNativeQuery(sql);

        if (null != params && params.size() > 0) {

            for (Map.Entry<String, Object> entry : params.entrySet()) {

                query.setParameter(entry.getKey(), entry.getValue());
            }
        }

        return query;
    }

    /**
     * SQL 查询LIST
     *
     * @param sql
     * @param params
     * @return
     */
    @Override
    public List getItemListByNativeSQL(final String sql, final Map<String, Object> params) {

        Query query = getCurrentSession().createNativeQuery(sql);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (entry.getValue() instanceof Collection<?>) {
                    query.setParameterList(entry.getKey(), (Collection<?>) entry.getValue());
                } else if (entry.getValue() instanceof Object[]) {
                    query.setParameterList(entry.getKey(), (Object[]) entry.getValue());
                } else {
                    query.setParameter(entry.getKey(), entry.getValue());
                }
            }
        }
        return query.list();
    }

    /**
     * SQL 查询LIST
     *
     * @param sql
     * @param clazz
     * @param params
     * @return
     */
    @Override
    public List getItemListByNativeSQL(String sql, Class clazz, Map<String, Object> params) {

        Query query = getCurrentSession().createNativeQuery(sql, clazz);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.list();
    }

    /**
     * 执行 HQL Query
     *
     * @param hql
     * @param params
     * @param firstResult
     * @param maxResults
     * @return
     */
    @Override
    public List execQueryList(final String hql, final Map<String, Object> params, int firstResult, int maxResults) {

        Query query = getCurrentSession().createQuery(hql);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResults);
        return query.list();
    }

    /**
     * 执行SQL 分页
     *
     * @param sql
     * @param params
     * @param firstResult
     * @param maxResults
     * @return
     */
    @Override
    public List execQueryListBySQL(String sql, Map<String, Object> params, int firstResult, int maxResults) {

        Query query = getCurrentSession().createNativeQuery(sql);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResults);
        return query.list();
    }

    /**
     * 获取分页数据
     *
     * @param sql    SQL语句
     * @param params
     * @param page   页号
     * @param rows   行数   @return
     */
    @Override
    public PageList getPageListByNativeSQL(String sql, Map<String, Object> params, int page, int rows) {

        Query query = getCurrentSession().createNativeQuery(sql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);

        return new PageList(query.list(), String.valueOf(getCount(sql, params)));
    }

    /**
     * 获取分页数据
     *
     * @param sql    SQL语句
     * @param params 参数
     * @param page   页号
     * @param rows   行数
     * @param clazz  实体对象(与表结构字段需对应)
     * @return
     */
    @Override
    public PageList getPageListByNativeSQL(String sql, Map<String, Object> params, int page, int rows, Class<?> clazz) {
        Query query = getCurrentSession().createNativeQuery(sql).setResultTransformer(Transformers.aliasToBean(clazz));
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);

        return new PageList(query.list(), String.valueOf(getCount(sql, params)));
    }

    /**
     * 获取分页数据
     *
     * @param sql
     * @param params
     * @param entityMap
     * @param scalarMap
     * @param joinMap
     * @param page
     * @param rows
     * @return
     */
    @Override
    public PageList getPageListByNativeSQL(String sql, Map<String, Object> params, Map<String, Class> entityMap, Map<String, Type> scalarMap, Map<String, String> joinMap, int page, int rows) {


        NativeQuery query = getCurrentSession().createNativeQuery(sql);

        if (null != params && params.size() > 0) {

            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }

        if (null != entityMap && entityMap.size() > 0) {

            for (Map.Entry<String, Class> entry : entityMap.entrySet()) {
                query.addEntity(entry.getKey(), entry.getValue());
            }
        }

        if (null != scalarMap && scalarMap.size() > 0) {

            for (Map.Entry<String, Type> entry : scalarMap.entrySet()) {
                query.addScalar(entry.getKey(), entry.getValue());
            }
        }

        if (null != joinMap && joinMap.size() > 0) {

            for (Map.Entry<String, String> entry : joinMap.entrySet()) {
                query.addJoin(entry.getKey(), entry.getValue());
            }
        }

        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);

        return new PageList(query.list(), String.valueOf(getCount(sql, params, entityMap, scalarMap, joinMap)));
    }


    public int getCount(String sql, Map<String, Object> params, Map<String, Class> entityMap, Map<String, Type> scalarMap, Map<String, String> joinMap) {

        NativeQuery query = getCurrentSession().createNativeQuery(sql);

        if (null != params && params.size() > 0) {

            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }

        if (null != entityMap && entityMap.size() > 0) {

            for (Map.Entry<String, Class> entry : entityMap.entrySet()) {
                query.addEntity(entry.getKey(), entry.getValue());
            }
        }

        if (null != scalarMap && scalarMap.size() > 0) {

            for (Map.Entry<String, Type> entry : scalarMap.entrySet()) {
                query.addScalar(entry.getKey(), entry.getValue());
            }
        }

        if (null != joinMap && joinMap.size() > 0) {

            for (Map.Entry<String, String> entry : joinMap.entrySet()) {
                query.addJoin(entry.getKey(), entry.getValue());
            }
        }

        return query.list().size();
    }


    /**
     * 获取数据条数
     *
     * @param sql
     * @param params
     * @return
     */
    public int getCount(String sql, Map<String, Object> params) {


        StringBuffer countSql = new StringBuffer();

        countSql.append(" SELECT COUNT(1) FROM ( ");
        countSql.append(sql);
        countSql.append(" ) as _count_tab WHERE  1 = 1 ");

        Query query = getCurrentSession().createNativeQuery(countSql.toString());

        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }

        Object valueObject = query.uniqueResult();

        if (valueObject instanceof BigDecimal) {

            return ((BigDecimal) valueObject).intValue();
        } else {
            return ((BigInteger) valueObject).intValue();
        }
    }

    /**
     * 获取数据条数
     *
     * @param hql
     * @param params
     * @return
     */
    public int getHqlCount(String hql, Map<String, Object> params) {


        String countSql = hql.replaceFirst("(from|From)\\s+", " FROM ");

        String sqls[] = countSql.split("FROM");

        sqls[0] = "SELECT COUNT(1) FROM ";

        StringBuffer stringBuffer = new StringBuffer();

        for (String str : sqls) {

            stringBuffer.append(str);
        }


        Query query = getCurrentSession().createQuery(stringBuffer.toString());

        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }

        return ((Long) query.uniqueResult()).intValue();
    }


    /**
     * 获取分页数据
     *
     * @param sql                   SQL语句
     * @param params
     * @param convertRowDataService 数据转换接口
     * @param page                  页号
     * @param rows                  行数    @return
     */
    @Override
    public PageList getPageListByNativeSQL(String sql, Map<String, Object> params, PageList.ConvertRowDataService convertRowDataService, int page, int rows) {

        PageList pageList = getPageListByNativeSQL(sql, params, page, rows);

        return new PageList(pageList.getRows(), pageList.getTotal(), convertRowDataService);
    }

    /**
     * 获取分页数据
     *
     * @param hql    HQL语句
     * @param params 参数
     * @param page   页号
     * @param rows   行数
     * @return
     */
    @Override
    public PageList getPageListByHQL(String hql, Map<String, Object> params, int page, int rows) {

        Query query = getCurrentSession().createQuery(hql);
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (entry.getValue() instanceof Collection<?>) {
                    query.setParameterList(entry.getKey(), (Collection<?>) entry.getValue());
                } else if (entry.getValue() instanceof Object[]) {
                    query.setParameterList(entry.getKey(), (Object[]) entry.getValue());
                } else {
                    query.setParameter(entry.getKey(), entry.getValue());
                }
            }
        }
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);

        return new PageList(query.list(), String.valueOf(getHqlCount(hql, params)));
    }

    /**
     * 使用 SQL 获取数据 返回 List<Map<String,Object>>
     *
     * @param sql    SQL
     * @param params 参数
     * @return List<Map                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               <                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               String                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Object>>
     */
    @Override
    public List<Map<String, Object>> getMapDataListByNativeSQL(String sql, Map<String, Object> params) {

        Query query = getCurrentSession().createNativeQuery(sql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        ;

        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.list();
    }

    /**
     * 通过SQL查询
     *
     * @param sql    SQL
     * @param params 参数
     * @return Map<String                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Object>
     */
    @Override
    public Map<String, Object> getSingleMapDataBySQL(String sql, Map<String, Object> params) {

        Query query = getCurrentSession().createNativeQuery(sql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        ;

        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return (Map<String, Object>) query.uniqueResult();
    }


    /**
     * 批量添加数据
     *
     * @param listData
     */
    @Override
    public void batchInsertObject(List<?> listData) throws Exception {

        batchInsertObject(listData, 100);
    }

    /**
     * 批量插入数据
     *
     * @param listData
     * @throws Exception
     */
    @Override
    public void batchsaveOrUpdateObject(List<?> listData) throws Exception {
        try {
            if (listData != null && listData.size() > 0) {
                for (int i = 0; i < listData.size(); i++) {
                    saveOrUpdate(listData.get(i));
                    if (i % 100 == 0) {
                        currentSessionflush();
                        currentSessionClear();
                    }
                }
            }
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 批量插入数据
     *
     * @param listData
     * @param batchSiz
     * @throws Exception
     */
    @Override
    public void batchInsertObject(List<?> listData, Integer batchSiz) throws Exception {
        try {
            if (listData != null && listData.size() > 0) {
                for (int i = 0; i < listData.size(); i++) {
                    saveObject(listData.get(i));
                    if (i % batchSiz == 0) {
                        currentSessionflush();
                        currentSessionClear();
                    }
                }
            }
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 批量插入数据
     *
     * @param sqlList
     * @throws Exception
     */
    @Override
    public void batchInsertSql(List<String> sqlList, Integer batchSiz) throws Exception {

        try {
            if (sqlList != null && sqlList.size() > 0) {
                for (int i = 0; i < sqlList.size(); i++) {

                    Query query = getCurrentSession().createNativeQuery(sqlList.get(i));
                    query.executeUpdate();

                    if (i % batchSiz == 0) {
                        currentSessionflush();
                        currentSessionClear();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * currentSessionflush
     */
    @Override
    public void currentSessionflush() {
        getCurrentSession().flush();
    }

    /**
     * currentSessionClear
     */
    @Override
    public void currentSessionClear() {
        getCurrentSession().clear();
    }


}
